package info.nixbear.tgcms.admin.client.activity;

import info.nixbear.tgcms.admin.client.AdminRemoteService;
import info.nixbear.tgcms.admin.client.AdminRemoteServiceAsync;
import info.nixbear.tgcms.admin.client.place.PagesPlace;
import info.nixbear.tgcms.admin.client.place.PagesPlace.PageEditPlace;
import info.nixbear.tgcms.admin.client.plupload.File;
import info.nixbear.tgcms.admin.client.plupload.Plupload;
import info.nixbear.tgcms.admin.client.plupload.PluploadListener;
import info.nixbear.tgcms.admin.client.view.PageEditView;
import info.nixbear.tgcms.admin.client.view.PageEditor;
import info.nixbear.tgcms.admin.shared.request.AdminRequestFactory;
import info.nixbear.tgcms.admin.shared.request.AttachmentProxy;
import info.nixbear.tgcms.admin.shared.request.PageProxy;
import info.nixbear.tgcms.admin.shared.request.PageRequest;
import info.nixbear.tgcms.admin.shared.request.StringTextKvpProxy;
import info.nixbear.tgcms.admin.shared.request.TemplateProxy;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.validation.ConstraintViolation;

import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.Location;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.inject.Inject;
import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryEditorDriver;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.RequestContext;

public class PageEditActivity extends AbstractActivity implements PluploadListener {

	Logger logger = Logger.getLogger(PageEditActivity.class.getName());
	RegExp regExp = RegExp.compile("^(https?:\\/\\/)([^\\/]*)(\\/.*)$");
	
	private PageEditView view;
	private PlaceController placeController;
	private AdminRequestFactory requestFactory;
	private PageEditPlace place;
	private Driver editorDriver;
	private ListDataProvider<File> uploadFilesDataProvider;
	private Queue<String> uploadUrls = new LinkedList<String>();
	private AdminRemoteServiceAsync adminRemoteService;

	interface Driver extends RequestFactoryEditorDriver<PageProxy, PageEditor> {
	}

	@Inject
	public PageEditActivity(PageEditView view, PlaceController placeController,
			AdminRequestFactory requestFactory) {
		this.view = view;
		this.placeController = placeController;
		this.requestFactory = requestFactory;

		uploadFilesDataProvider = new ListDataProvider<File>(new ArrayList<File>());
		uploadFilesDataProvider.addDataDisplay(view.getFilesToUploadTable());
		adminRemoteService = GWT.create(AdminRemoteService.class);
	}

	public PageEditActivity withPlace(PageEditPlace place) {
		this.place = place;
		return this;
	}

	@Override
	public void start(final AcceptsOneWidget panel, EventBus eventBus) {
		view.setPageEditActivity(this);

		this.requestFactory.templateRequest().listAll().fire(new Receiver<List<TemplateProxy>>() {

			@Override
			public void onSuccess(List<TemplateProxy> response) {
				view.getEditor().setTemplates(response);

				edit(panel);
			}
		});

	}

	private void edit(final AcceptsOneWidget panel) {

		editorDriver = GWT.create(Driver.class);
		editorDriver.initialize(requestFactory, view.getEditor());

		if (place.isTokenANewPageToken()) {
			PageRequest request = requestFactory.pageRequest();
			PageProxy page = request.create(PageProxy.class);
			page.setAttributesList(new ArrayList<StringTextKvpProxy>());
			page.setAttachments(new ArrayList<AttachmentProxy>());

			edit(panel, page, request);
		} else {

			PageProxy page = place.getPage();

			@SuppressWarnings("unchecked")
			Request<PageProxy> fetchRequest = (Request<PageProxy>) ((page == null) ? requestFactory
					.pageRequest().get(Long.valueOf(place.getToken())) : requestFactory.find(page
					.stableId()));

			fetchRequest.with(editorDriver.getPaths());
			fetchRequest.to(new Receiver<PageProxy>() {
				@Override
				public void onSuccess(PageProxy page) {

					PageRequest request = requestFactory.pageRequest();

					edit(panel, page, request);
				}
			}).fire();
		}

	}

	public void edit(AcceptsOneWidget panel, PageProxy page, PageRequest request) {
		editorDriver.edit(page, request);
		view.getEditor().edit(page, request);
		view.getEditor().updateAttributesTable();
		view.getEditor().updateAttachmentsTable();

		request.put(page);

		panel.setWidget(view.asWidget());
	}

	public void goTo(Place place) {
		placeController.goTo(place);
	}

	public void onUplaod(ClickEvent event) {
		adminRemoteService.getBlobstoreUploadUrls("key", uploadFilesDataProvider.getList().size(),
				new AsyncCallback<List<String>>() {

					@Override
					public void onSuccess(List<String> result) {
						uploadUrls.addAll(result);
						if (view.plupload != null) {
							view.plupload.start();
						}
					}

					@Override
					public void onFailure(Throwable caught) {
						// TODO Auto-generated method stub

					}
				});
	}

	public void onSave(ClickEvent event) {
		// Flush the contents of the UI
		RequestContext context = editorDriver.flush();

		// Check for errors
		if (editorDriver.hasErrors()) {
			Window.alert("Errors detected locally");
			return;
		}

		// Send the request
		context.fire(new Receiver<Void>() {
			@Override
			public void onConstraintViolation(Set<ConstraintViolation<?>> violations) {
				// Otherwise, show ConstraintViolations in the UI
				Window.alert("Errors detected on the server");
				editorDriver.setConstraintViolations(violations);
			}

			@Override
			public void onSuccess(Void response) {
				placeController.goTo(new PagesPlace());
			}
		});
	}

	public void onCancel(ClickEvent event) {
		placeController.goTo(new PagesPlace());
	}

	@Override
	public void onStop() {
		view.setPageEditActivity(null);
		view.getEditor().stop();
	}

	@Override
	public void onInit(Plupload pl, String runtime) {
		logger.log(Level.INFO, "Initialized Plupload with runtime " + runtime + ".");
	}

	@Override
	public void postInit(Plupload p) {
	}

	@Override
	public void onFilesAdded(Plupload p, List<File> files) {
		uploadFilesDataProvider.getList().addAll(files);
	}

	@Override
	public void onFilesRemoved(Plupload p, List<File> files) {
	}

	@Override
	public void onQueueChanged(Plupload p) {
	}

	@Override
	public void onRefresh(Plupload p) {
	}

	@Override
	public void onStateChanged(Plupload p) {
	}

	@Override
	public void onBeforeUpload(Plupload pl, File file) {
		String uploadUrl = uploadUrls.poll();
		uploadUrl = changeHostToCurrent(uploadUrl);
		pl.setUploadUrl(uploadUrl);
		logger.log(Level.INFO, "Changed upload url to " + uploadUrl + " for file " + file.getName());
	}

	private String changeHostToCurrent(String from) {
		String to = from;
		MatchResult matcher = regExp.exec(from);
		boolean matchFound = (matcher != null);
		if (matchFound) {
			to = matcher.getGroup(1) + Location.getHost() + matcher.getGroup(3);
		}
		logger.finer("Change url host to current, from "+from+" to "+to);
		return to;
	}

	@Override
	public void onFileUpload(Plupload p, File file) {
	}

	@Override
	public void onFileUploadProgress(Plupload p, File file) {
		uploadFilesDataProvider.refresh();
	}

	@Override
	public void onFileUploaded(Plupload p, File file, JavaScriptObject serverResponse) {
		String response = parseResponse(serverResponse);
		logger.log(Level.INFO, "File uploaded, server response: " + response);
		JSONValue jsonValue = JSONParser.parseStrict(response);
		JSONObject jsonObject = jsonValue.isObject();
		AttachmentProxy newAttachment = view.getEditor().request.create(AttachmentProxy.class);
		newAttachment.setTitle(file.getName());
		newAttachment.setFileName(file.getName());
		newAttachment.setFileKey(jsonObject.get("blobKey").isString().stringValue());
		String servingUrl = jsonObject.get("servingUrl").isString().stringValue();
		newAttachment.setServingUrl(changeHostToCurrent(servingUrl));
		view.getEditor().attachmentsDataProvider.getList().add(newAttachment);
	}

	protected native String parseResponse(JavaScriptObject response) /*-{
		return response.response;
	}-*/;

	@Override
	public void onChunkUploaded(Plupload p, File file, JavaScriptObject response) {
	}

	@Override
	public void onError(Plupload p, JavaScriptObject error) {
	}

}
